package com.sslite.util;

import java.io.File;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.josql.Query;
import org.josql.QueryResults;

import com.sslite.model.IntraDayStructure;
import com.sslite.model.MetaData;

public class ReportUtil {
	private MetaData metaData;
	private Map<Date, List<IntraDayStructure>> mapDateIds = null;
	
	public ReportUtil(MetaData metaData) {
		this.metaData = metaData;
	}

	public Map<Date, List<IntraDayStructure>> getMapDateIds() {
		if( mapDateIds == null ){
			try{
				String intraDayData = Utility.getContent( "file:"+SSLiteProperties.ONE_MINUTE_DATA_FOLDER.getValue() +metaData.getSymbol()+"-INTRA-DAY" );
				mapDateIds = transformIntraDayToMap(intraDayData, true);
			}
			catch(Exception e){
				System.out.println( "ReportUtil: Exception in pulling/transforming one.minute data for " +metaData.getSymbol() );
				e.printStackTrace();
			}
		}
		return mapDateIds;
	}
	
	public List<IntraDayStructure> getIntraDayStructureList(){
		final List<IntraDayStructure> idsList = new ArrayList<IntraDayStructure>();
		for( List<IntraDayStructure> list : getMapDateIds().values() ){
			idsList.addAll( list );
		}
		return idsList;
	}

	public void generateSnapshotFor(Date date) throws Exception{
		final StringBuilder sb = new StringBuilder();
		try{
			final List<IntraDayStructure> idsList = this.getMapDateIds().get(date);
			// Start: Top n
			QueryResults qrTopN = Query.parseAndExec("SELECT * FROM com.sslite.model.IntraDayStructure order by volume desc", new ArrayList<IntraDayStructure>(idsList) );
			List<IntraDayStructure> results = qrTopN.getResults();
			
			final ArrayList<Integer> topIndices = new ArrayList<Integer>();
			for( int i=0; i<20 && results.size() > i; i++ ){
				topIndices.add( results.get(i).getIndex() );
			}
			// End: Top n
			
			StringBuilder sbTable = new StringBuilder( "<table border=1><tr><td>&nbsp;</td> <td>index</td> <td>time</td> <td>%change from prev</td> <td>open</td> <td>high</td> <td>low</td> <td>close</td> <td>volume</td> <td>CxV</td></tr>\n" );
			IntraDayStructure idsPrev = null;
			for( final IntraDayStructure ids : idsList ){
				String pcChange = "";
				if( idsPrev != null ){
					double change = Utility.round( ((ids.getClose() - idsPrev.getClose()) / idsPrev.getClose()) * 100.00 );
					pcChange = change+"%";
				}
				
				if( topIndices.contains( ids.getIndex() ) ){
					sbTable.append("<tr " +"style='background-color:#DADADA;color:" +(pcChange.startsWith("-") ? "red" : "green")+ "'"+ ">");
				}else{
					sbTable.append("<tr>");
				}
				
				sbTable.append("<td>").append("<input type=checkbox>").append("</td>");
				sbTable.append("<td>").append(ids.getIndex()).append("</td>");
				sbTable.append("<td>").append( new Date( ids.getTime() ) ).append("</td>");
				sbTable.append("<td align=right>").append( pcChange ).append("&nbsp;</td>");
				sbTable.append("<td>").append( ids.getOpen() ).append("</td>");
				sbTable.append("<td>").append( ids.getHigh() ).append("</td>");
				sbTable.append("<td>").append( ids.getLow() ).append("</td>");
				sbTable.append("<td>").append( ids.getClose() ).append("</td>");
				sbTable.append("<td>").append( Utility.getFormattedInteger( ids.getVolume() ) ).append("</td>");
				sbTable.append("<td>").append( "$" +Utility.getFormattedInteger( ids.getClose() * ids.getVolume() ) ).append("</td>");
				sbTable.append("</tr>\n");
				idsPrev = ids;
			}
			sbTable.append( "</table>\n" );
			sb.append( sbTable );
		}
		catch(Exception e){
			sb.append( "Exception in getting IntraDayStructure for Symbol: " +metaData.getSymbol()+ ", Date: " +date+ ". Details: " +e.getMessage() );
			e.printStackTrace();
		}
		
		File folder = new File(SSLiteProperties.RPT_INTRA_DAY_SNAPSHOT_FOLDER.getValue() +metaData.getSymbol());
		if( !folder.exists() ){
			folder.mkdir();
		}
		String path = SSLiteProperties.RPT_INTRA_DAY_SNAPSHOT_FOLDER.getValue() +metaData.getSymbol()+"/"+metaData.getSymbol()+ "_" +Utility.getStrDate( date, "MM_dd_yyyy" )+ ".html";
		Utility.saveContent(path, sb.toString());
	}


	private Map<Date, List<IntraDayStructure>> transformIntraDayToMap(String data, boolean isIntraDay) throws Exception{
		int index = 0;
		final int INDX_D = 0;
		final int INDX_C = 1;
		final int INDX_H = 2;
		final int INDX_L = 3;
		final int INDX_O = 4;
		final int INDX_V = 5;
		
		final Long DATE_OFFSET = 1000L;
		final String[] rows = data.split("\n");
		final Map<Date, List<IntraDayStructure>> map = new TreeMap<Date, List<IntraDayStructure>>(new Comparator<Date>() {
			public int compare(Date o1, Date o2) {
				return o1.compareTo(o2);
			}
		});
		List<IntraDayStructure> list = null;
		boolean bDataStarted = false;
		Date date = null, previousDate = null;
		for( String row : rows ){
			try{
				if( row.startsWith("a") ){
					String[] cols = row.split(",");
					date = new Date( Long.parseLong(cols[INDX_D].substring(1)) * DATE_OFFSET );
					
					if( previousDate == null || !Utility.areDatesEqual(previousDate, date) ){
						list = new LinkedList<IntraDayStructure>();
						previousDate = date;
						map.put(Utility.getDate( Utility.getStrDate(date)), list);
					}
	
					final Double close = Double.parseDouble(cols[INDX_C]);
					final Double high  = Double.parseDouble(cols[INDX_H]);
					final Double low   = Double.parseDouble(cols[INDX_L]);
					final Double open  = Double.parseDouble(cols[INDX_O]);
					final Long volume  = Long.parseLong(cols[INDX_V]);
	
					list.add(new IntraDayStructure(++index, date.getTime(), close, high, low, open, volume));
	
					bDataStarted = true;
				}else if( bDataStarted ){
					String[] cols = row.split(",");
					
					if( cols.length != 6 ){
						System.out.println( "ReportUtil (" +metaData.getSymbol()+ "): Error parsing current row. Data -> " +row );
						continue;
					}
					
					Date adjustedDate = null;
					if( isIntraDay ){
						adjustedDate = new Date( date.getTime() +Long.parseLong(cols[INDX_D]) );
					}else{
						adjustedDate = new Date( date.getTime() +(Long.parseLong(cols[INDX_D]) * 60 * 1000 * 60 * 24 ) );
					}
					
					if( previousDate == null || !Utility.areDatesEqual(previousDate, adjustedDate) ){
						list = new LinkedList<IntraDayStructure>();
						previousDate = adjustedDate;
						map.put(Utility.getDate( Utility.getStrDate(adjustedDate)), list);
					}
					
					
					final Double close = Double.parseDouble(cols[INDX_C]);
					final Double high  = Double.parseDouble(cols[INDX_H]);
					final Double low   = Double.parseDouble(cols[INDX_L]);
					final Double open  = Double.parseDouble(cols[INDX_O]);
					final Long volume  = Long.parseLong(cols[INDX_V]);
	
					list.add(new IntraDayStructure(++index, adjustedDate.getTime(), close, high, low, open, volume));
				}
			}
			catch(Exception e){
				System.out.println( "ReportUtil (" +metaData.getSymbol()+ "): Warning: Exception occured possibly due to bad data: row --> " +row );
			}
		}
		return map;
	}
}
